package gox

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"gitee.com/burningsong/golib/pkg/errx"
)

// AwakeCronWorker 支持立即唤醒的定时任务
// 目标任务任意时刻只能有一个在执行
type AwakeCronWorker struct {
	name string
	// 保活，存储协程意外退出
	keepAliveChan  chan struct{}
	shutdownSignal chan os.Signal
	// 程序收到退出信号
	isShutdown bool
	// 停止保活
	stopKeepAliveChan chan struct{}
	//定时检查时间
	cronDuration time.Duration
	//是否正在更新任务
	isUpdateTask    bool
	updateTaskMutex sync.Mutex
	//新任务通知（更新在等待时，快速开始）
	newTaskCh chan struct{}
	taskFunc  func()
	alertFunc func(title, detail string)
}

func NewAwakeCronWorker(name string, cronDuration time.Duration, taskFunc func(), alertFunc func(title, detail string)) *AwakeCronWorker {
	return &AwakeCronWorker{
		name:              name,
		keepAliveChan:     make(chan struct{}),
		shutdownSignal:    make(chan os.Signal, 1),
		isShutdown:        false,
		stopKeepAliveChan: make(chan struct{}),
		cronDuration:      cronDuration,
		isUpdateTask:      false,
		updateTaskMutex:   sync.Mutex{},
		newTaskCh:         make(chan struct{}, 1),
		taskFunc:          taskFunc,
		alertFunc:         alertFunc,
	}
}

func (w *AwakeCronWorker) Start() {
	signal.Notify(w.shutdownSignal, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	w.keepAlive()
	w.run()
}

func (w *AwakeCronWorker) run() {
	go func() {
		ticker := time.NewTicker(w.cronDuration)
		defer func() {
			if err := recover(); err != nil {
				w.alert(fmt.Sprintf("%s worker run recover", w.name), fmt.Sprintf("%s AwakeCronWorker run recover error:%+v,msg:%s", w.name, err, errx.GetStack()))
			}
			ticker.Stop()
			if !w.isShutdown {
				time.Sleep(time.Second)
				w.keepAliveChan <- struct{}{}
			}
		}()

		for {
			select {
			case <-w.newTaskCh:
				w.startUpdateTask()
			case <-ticker.C:
				w.startUpdateTask()
			case <-w.shutdownSignal:
				//TODO:清理工作

				w.isShutdown = true
				w.stopKeepAliveChan <- struct{}{}
				return
			}
		}
	}()
}

func (w *AwakeCronWorker) startUpdateTask() {
	w.updateTaskMutex.Lock()
	if w.isUpdateTask {
		w.updateTaskMutex.Unlock()
		return
	}

	w.isUpdateTask = true
	w.updateTaskMutex.Unlock()

	go func() {
		defer func() {
			if err := recover(); err != nil {
				w.alert(fmt.Sprintf("%s worker run recover", w.name), fmt.Sprintf("%s AwakeCronWorker run recover error:%+v,msg:%s", w.name, err, errx.GetStack()))
			}

			w.updateTaskMutex.Lock()
			w.isUpdateTask = false
			w.updateTaskMutex.Unlock()
		}()

		w.taskFunc()
	}()
}

func (w *AwakeCronWorker) NotifyNewTask() {
	select {
	case w.newTaskCh <- struct{}{}:
	default:
	}
}

func (w *AwakeCronWorker) keepAlive() {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				w.alert(fmt.Sprintf("%s worker run recover", w.name),
					fmt.Sprintf("%s keepAlive recover error:%+v,msg:%s", w.name, err, errx.GetStack()))
			}
		}()

		for {
			select {
			case <-w.keepAliveChan:
				w.run()
			case <-w.stopKeepAliveChan:
				w.alert(fmt.Sprintf("%s worker run closed", w.name),
					fmt.Sprintf("%s keepAlive exit", w.name))
				return
			}
		}
	}()
}

// Stop 主动停止
func (w *AwakeCronWorker) Stop() {
	w.shutdownSignal <- syscall.SIGHUP
}

func (w *AwakeCronWorker) alert(title, detail string) {
	if w.alertFunc != nil {
		w.alertFunc(title, detail)
	}
}
